$TITLE ('RTX-51 WAIT SYSTEM CALL')
$SYMBOLS
$NOXREF
$NOCOND
$NOMOD51
$NORB
$PAGELENGTH(80) PAGEWIDTH(110)
;************************************************************************
;*									*
;*    R T X - 5 1  :   WAIT SYSTEM CALL                                 *
;*							     		*
;*----------------------------------------------------------------------*
;*                                                                      *
;*    Filename     :   RTXWAIT.A51                                      *
;*    Language     :   Keil A-51                                        *
;*    Dev. system  :   Keil uVision2                                    *
;*    Targetsystem :   Any system based upon 8051 up                    *
;*                                                                      *
;*    Purpose      :   Implements the os_wait system call               *
;*								     	*
;*----------------------------------------------------------------------*
;* Rev. |  Released   | Programmer    | Comments                        *
;*----------------------------------------------------------------------*
;* 0.1  |  24.1.1991  | E. Glatz      | Changes for V3.20               *
;* 0.2  |   8.3.1991  | Th. Fischler  | C-51 parameter passing          *
;*      |  17.5.1991  |               | P2 addressing eliminated        *
;*      |   9.7.1991  |               | Disable all interrupts while    *
;*      |             |               | mailbox accesses                *
;*      |   9.10.1991 | Th. Fischler  | Dummy labels os_wait1, os wait2 *
;*      |             |               | added                           *
;*      |  4.06.1992  |               | RTXINS.INC converted to proc.   *
;* 5.0  | 10.10.1994  | E. Glatz      | Label _os_wait1 removed         *
;*      | 12.10.1994  |               | Semaphore support added         *
;* 7.00 | 13.11.2001  | T. Mueller    | Release 7.00                    *
;************************************************************************
;*    Copyright 1991 .. 2001 METTLER  &  FUCHS  AG,  CH-8953 Dietikon   *
;************************************************************************

$INCLUDE (RTXDEBUG.INC)
$INCLUDE (RTXIMASK.INC)

;*----------------------------------------------------------------------*
;*	I M P O R T S
;*----------------------------------------------------------------------*

$INCLUDE (RTXCONST.INC)
$INCLUDE (RTXEXTRN.INC)

;       FROM RTXDISP
EXTRN   CODE    (?RTX_REDISPATCH)

;	FROM RTXBLOCK
EXTRN   CODE    (?RTX_NDBLOCK, ?RTX_DBLOCK)

;       FROM RTXQUOP
EXTRN   CODE    (?RTX_SIG_ENQUEUE, ?RTX_MSG_ENQUEUE, ?RTX_INT_ENQUEUE)
EXTRN   CODE    (?RTX_TMO_ENQUEUE, ?RTX_IVL_ENQUEUE, ?RTX_SEM_ENQUEUE)
EXTRN   CODE    (?RTX_TMO_DEQUEUE, ?RTX_SIG_DEQUEUE)
EXTRN   CODE    (?RTX_INTSIG_DEQUEUE, ?RTX_MSG_DEQUEUE, ?RTX_SEM_DEQUEUE)

;        FROM RTXINS
EXTRN    CODE  (?RTX_INSERT_RDY_PRIO)


;*----------------------------------------------------------------------*
;*	E X P O R T S
;*----------------------------------------------------------------------*

PUBLIC  _os_wait, _os_wait2



;*----------------------------------------------------------------------*
;*	P R O C E D U R E S
;*----------------------------------------------------------------------*

;*----------------------------------------------------------------------*
;*      O S _ W A I T
;*----------------------------------------------------------------------*
;*      System call for event waiting.
;*----------------------------------------------------------------------*
;*      Input:
;*                 R7 : dcba'YYYY (event)
;*                      a: event = message/semaphore
;*                 	b: event = interrupt		   
;*                 	c: event = signal 		   
;*                      d: event = timeout
;*                      YYYY: mailbox number (0..7)  
;*                            ** or **
;*                            semaphore number (8..15)
;*	   
;*                 R5 : timeout or interval
;*                      - is timeout, if event bit "d" is set (=1)
;*                        = 0      --> no timeout (=conditional wait)
;*                        = 1..254 --> timeout in system ticks
;*                        = 255    --> wait forever,task not in-
;*                                     serted in delay list
;*                      - is interval, if event bit "d" is reset (=0)
;*                        = 0      --> no interval wait (see note below;
;*                                     internally changed to 255)
;*                        = 1..254 --> interval in system ticks
;*                        = 255    --> no interval wait (see note below)
;*                      NOTE: if no timeout or interval wait is desired,
;*                            then this parameter MUST HAVE a value of 0 
;*                            or 255 !
;*                 			   		   
;*                 R2 : HIGH (msg_pointer)
;*                 R3 : LOW  (msg_pointer)
;*                             --> pointer to external RAM variable 
;*                                 (word size) where to store the 
;*                                 message (high byte first, then low byte)
;*                      NOTE: if no wait for message is specified, then this
;*                            parameter may be left undefined.
;*
;*      Output:  - Completion status returned in R7 :
;*                      = #MSG_EVENT  --> message received
;*                      = #INT_EVENT  --> interrupt occured
;*                      = #SIG_EVENT  --> signal received
;*                      = #TMO_EVENT  --> timeout or interval event
;*                      = #NOT_OK     --> no messages & task wait list full
;*
;*               - (msg_pointer) if it is a message event, the message is 
;*                               stored at the ext. RAM address passed to 
;*                               RTX_WAIT as a parameter (see above)
;*----------------------------------------------------------------------*
;*      Internal register usage:
;*          R4 : timeout
;*----------------------------------------------------------------------*

?RTX?_os_wait?RTXWAIT  SEGMENT CODE
                          RSEG    ?RTX?_os_wait?RTXWAIT

_os_wait:
_os_wait2:
               DBG_SYS_ENTRY
               RTX_EXCLUDE_INT
               SETB  ?RTX_SUPERVISOR_MODE       ; Supervisor_mode := on
               MOV   A, R5
               MOV   R4, A                      ; store timeout to R4
	       RTX_ALLOW_DRV_INT

               ; Set up pointer to task_state
               MOV   A, ?RTX_RUNNING_TID
               ANL   A, #1FH
               RL    A
               RL    A
               ADD   A, #LOW(?RTX_STATE_TABLE)
               MOV   DPL, A
               MOV   DPH, #HIGH (?RTX_STATE_TABLE) ; DPTR-->task_state[task_no]
               ; Ensure R4 = 0FFH, if R4=0 and event bit "d"=0
               MOV   A, R7
               JB    ACC.7, TMO
               CJNE  R4, #0, TMO
               MOV   R4, #0FFH                  ; R4=0 --> R4=0FFH
TMO:                                    
               RTX_EXCLUDE_INT

               POP   ?RTX_TMP1                  ; Make free 2 bytes of stack
               POP   ?RTX_TMP2                  ; for subroutine call
                                                
               ; Check if wait for signal requested
               JNB   ACC.6, MSG_WAIT_CHK

               ; DPTR->state_table[task no]
               ; R4 contains timeout value
               CALL  ?RTX_SIG_ENQUEUE
               ; DPTR, R4 preserved
               ; Check if a signal is pending
               JNZ   SIGNAL_EVENT

               ; Check if wait for message requested
MSG_WAIT_CHK:  MOV   A, R7
               JNB   ACC.4, INT_WAIT_CHK
               JB    ACC.3, SEM_WAIT_CHK        ; check if sem/mbx no in [8..15]
               ; sem/mbx no is in [0..7]: it is a mailbox wait per def.
               ; DPTR --> state_table[task no]
               ; R4 contains timeout value
               ; R2/R3 contain msg. var. addr.
               ; Save DPTR
               MOV   ?RTX_TMP3, DPH
               MOV   ?RTX_TMP4, DPL

               ; Enqueue ?RTX_RUNNING_TID
               CALL  ?RTX_MSG_ENQUEUE
               ; DPTR --> MBX#_COUNT
               ; R4, R2, R3 preserved
               ; All interrupts are disabled (GLOBAL_INT_DISABLE)

               ; Check if a message is available
               JNZ   NO_MSG_AVAIL

               ; Read message from mailbox
               JMP   MSGS_WAIT
SEM_WAIT_CHK:                   
               ; sem/mbx no is in [8..15]: it is a semaphore wait per def.
               ; DPTR --> state_table[task no]
               ; R4 contains timeout value
               ; R2/R3 contain msg. var. addr.
               ; Save DPTR
               MOV   ?RTX_TMP3, DPH
               MOV   ?RTX_TMP4, DPL

               ; Enqueue ?RTX_RUNNING_TID
               CALL  ?RTX_SEM_ENQUEUE

               ; Evaluate return code of enqueue op.
               JNZ   NO_TKN_AVAIL

               ; Token available: token flag cleared by enqueue op. 
               ; Dequeue from signal list if already queued
               MOV   DPH, ?RTX_TMP3             ; DPTR->task_state[running_tid]
               MOV   DPL, ?RTX_TMP4
               CALL  ?RTX_SIG_DEQUEUE

	       MOV   R7, #SEM_EVENT             ; status = SEM_EVENT
	       JMP   EXIT
NO_TKN_AVAIL:
NO_MSG_AVAIL:  ; restore DPTR
               MOV   DPH, ?RTX_TMP3
               MOV   DPL, ?RTX_TMP4
               ; Check if task wait list of mailbox/semaphore is full
               CJNE  A, #1, WAIT_LIST_FULL

               ; Check if wait for interrupt requested
INT_WAIT_CHK:  MOV   A, R7
               JNB   ACC.5, TMO_WAIT_CHK

	       ; DPTR --> state_table[task no]
               ; R4 contains timeout value
               ; Enqueue ?RTX_RUNNING_TID
               CALL ?RTX_INT_ENQUEUE
               ; DPTR, R4 preserved

	       ; Check if wait for timeout requested
TMO_WAIT_CHK:  MOV   A, R7
	       JNB   ACC.7, IVL_WAIT_CHK
	       ; DPTR --> state_table[task no]
	       ; R2 contains timeout value
               ; Enqueue ?RTX_RUNNING_TID
               MOV   A, R4
	       MOV   R2, A
               CALL ?RTX_TMO_ENQUEUE
	       ; Check if zero timeout
	       JZ    TIMEOUT_EVENT
               SJMP  BLOCK

               ; Check if wait for interval requested
IVL_WAIT_CHK:  CJNE  R4, #0FFH, IVL_ENQ
               SJMP  BLOCK
IVL_ENQ:       ; DPTR --> state_table[task no]
	       ; R2 contains timeout value
               ; Enqueue ?RTX_RUNNING_TID
               MOV   A, R4
	       MOV   R2, A
               CALL ?RTX_IVL_ENQUEUE
	       ; Check if zero timeout (interval already passed)
	       JZ    INTERVAL_EVENT

	       ; Block task
BLOCK:          
               PUSH  ?RTX_TMP2                  ; Restore return address
               PUSH  ?RTX_TMP1                  ; to stack
               JB    ?RTX_RUNNING_TID.4, DRBLOCK

	       ; Block task with reduced context, do not save R5..R7
	       MOV   R3, #0
               JMP   ?RTX_NDBLOCK

DRBLOCK:       JMP   ?RTX_DBLOCK

;
;       Signal event handling
;
SIGNAL_EVENT:	
	       MOV   R7, #SIG_EVENT		; Status = signal event
EXIT:		
               PUSH  ?RTX_TMP2                  ; Restore return address
               PUSH  ?RTX_TMP1                  ; to stack
               JB    ?RTX_REQ_REDISPATCH, REDISP
               CLR   ?RTX_SUPERVISOR_MODE       ; Supervisor_mode := off
	       DBG_SYS_EXIT
	       RTX_ALLOW_INT
	       RET

REDISP:        CLR   ?RTX_FULL_CONTEXT
               JMP   ?RTX_REDISPATCH
;
;       Interval event handling
;
INTERVAL_EVENT:
               ; Dequeue from sig/int/msg/sem wait lists
               ; (DPTR preserved by IVL_ENQUEUE)
               MOV   ?RTX_TMP0, ?RTX_RUNNING_TID
	       ; DPTR --> task_state[running_tid]
               ; ?RTX_TMP0 holds task id
               CALL  ?RTX_INTSIG_DEQUEUE
               ; DPTR, ?RTX_TMP0 preserved
               CALL  ?RTX_MSG_DEQUEUE
               JZ    DEQDONE                    ; Eval ret code if deq done
                                                ; DPTR preserved if no deq done
                                                ; ?RTX_TMP0 preserved
               CALL  ?RTX_SEM_DEQUEUE           
                                                ; ?RTX_TMP0 preserved
DEQDONE:
;
;       Timeout/interval event handling
;
TIMEOUT_EVENT:
	       MOV   R7, #TMO_EVENT
	       JMP   EXIT
;
;       Wait list full handling
;       (very unlikely to happen)
WAIT_LIST_FULL:
	       ; DPTR --> task_state[running_tid]
	       ; Dequeue from signal list if already queued
               CALL   ?RTX_SIG_DEQUEUE
	       ; Status = 'nf wait list full' or 'sem wait list full'
	       MOV   R7, #NOT_OK
	       JMP   EXIT
;
;	Message event handling
;
MSGS_WAIT:     ; (?RTX_TMP3,?RTX_TMP4) --> task_state[running_tid]
	       ; DPTR --> MBX#_COUNT
               ; all interrupts are disabled (GLOBAL_INT_DISABLE)

	       ; MBX#_COUNT=MBX#_COUNT-1
	       MOVX  A, @DPTR
	       DEC   A
	       MOVX  @DPTR, A

	       ; Set up DPTR as pointer to mailbox message list
	       INC   DPL
	       INC   DPL
	       MOVX  A, @DPTR			; Get MBX#_FIRST
	       MOV   R1, DPL			; save ptr to MBX#_FIRST
	       MOV   DPL, A
               MOV   DPH, #HIGH(?RTX_MBX_PAGE)

	       ; R6 := HIGH (message)
	       MOVX  A, @DPTR
	       MOV   R6, A
	       ; Increment FIFO pointer
	       INC   DPL
	       ANL   DPL, #0EFH
	       ; R7 := LOW (message)
	       MOVX  A, @DPTR
	       MOV   R7, A

	       ; Increment FIFO pointer
	       MOV   A, DPL
	       INC   A
	       ANL   A, #0EFH
	       MOV   DPL, R1
               MOV   DPH, #HIGH(?RTX_MBX_CONTROL_BASE)
	       MOVX  @DPTR, A			; Store new MBX#_FIRST

               ; Mailbox variables updated, reenable the ISR-Interrupts
               GLOBAL_INT_ENABLE

	       ; Set up DPTR as pointer to message return location
	       MOV   DPH, R2
	       MOV   DPL, R3
	       ; Store high byte of message
	       MOV   A, R6
	       MOVX  @DPTR, A
	       ; Store low byte of message
	       INC   DPTR
	       MOV   A, R7
	       MOVX  @DPTR, A

               ; Dequeue from signal list if already queued
               MOV   DPH, ?RTX_TMP3             ; DPTR->task_state[running_tid]
               MOV   DPL, ?RTX_TMP4
               CALL  ?RTX_SIG_DEQUEUE

	       ; Check if any tasks wait for non full message list
	       DEC   R1
	       MOV   DPL, R1
               MOV   DPH, #HIGH(?RTX_MBX_CONTROL_BASE)
	       MOVX  A, @DPTR
	       JNZ   TWAIT			; Jump if MBX#_TCOUNT > 0
	       MOV   R7, #MSG_EVENT		; Status = message received
	       JMP   EXIT

	       ; Dequeue first non full wait task from nf wait list
TWAIT:	       DEC   A
	       MOVX  @DPTR, A			; MBX#_TCOUNT=MBX#_TCOUNT-1
	       INC   DPL
	       INC   DPL
	       INC   DPL
	       MOV   R1, DPL
	       MOVX  A, @DPTR
	       ; (DPTR) --> nf wait FIFO
	       MOV   DPL, A
               MOV   DPH, #HIGH(?RTX_MBX_PAGE)
	       MOVX  A, @DPTR			; Get task id
               MOV   ?RTX_TMP0, A               ; Save task id in ?RTX_TMP0
	       ; Increment FIFO pointer
	       MOV   A, DPL
	       ANL   A, #00FH			; Check if at end of FIFO
	       CJNE  A, #00FH, NO_WRAP
	       MOV   A, DPL			; Get back old ptr value
	       ANL   A, #0F0H			; Rewind ptr to FIFO start
	       JMP   INC_DONE
NO_WRAP:       MOV   A, DPL			; Get back old ptr value
	       INC   A				; Just inc., not at buffer end
INC_DONE:      MOV   DPL, R1
               MOV   DPH, #HIGH(?RTX_MBX_CONTROL_BASE)
	       MOVX  @DPTR, A			; MBX#_TFIRST=MBX#_TFIRST+1

	       ; Set up ptr to task state
               MOV   A, ?RTX_TMP0
	       ANL   A, #01FH
	       RL    A
	       RL    A
               ADD   A, #LOW (?RTX_STATE_TABLE)
	       MOV   DPL, A
               MOV   DPH, #HIGH (?RTX_STATE_TABLE) ; DPTR-->state_table[task_no]

               CALL ?RTX_TMO_DEQUEUE

	       ; Reset nf wait bit
	       INC   DPL
	       MOVX  A, @DPTR			; Get second task state byte
	       ANL   A, #07FH			; Clear WN bit
	       MOVX  @DPTR, A			; Store second task state byte

	       ; Store return status in context of non full wait task
	       ; fast and standard task context must be handled differently
               MOV   A, ?RTX_TMP0
	       JB    ACC.4, DRIV_CONTEXT

	       ; Store in standard task (non driver task) context
	       ANL   A, #0FH
	       MOV   B, A
               MOV   A, #LOW(?RTX_KNTXT_PTR_TABLE)
	       ADD   A, B
	       ADD   A, B
	       MOV   DPL, A
               MOV   DPH, #HIGH(?RTX_KNTXT_PTR_TABLE)
	       MOVX  A, @DPTR
	       MOV   B, A
	       INC   DPL
	       MOVX  A, @DPTR
	       MOV   DPH, B
	       MOV   DPL, A
	       MOV   A, #NFL_EVENT
	       INC   DPL
	       MOVX  @DPTR, A			; Store return status
	       JMP   MAKE_RDY

	       ; Store in fast task (driver task) context
DRIV_CONTEXT:  JB    ACC.1, DRIV3
	       JB    ACC.0, DRIV2

	       MOV   RB1R7, #NFL_EVENT		; Store return status
	       JMP   MAKE_RDY

DRIV2:	       MOV   RB2R7, #NFL_EVENT
	       JMP   MAKE_RDY

DRIV3:	       MOV   RB3R7, #NFL_EVENT

MAKE_RDY:      ; Insert task in ready list
               MOV   A, ?RTX_TMP0
               CALL  ?RTX_INSERT_RDY_PRIO

               MOV   R7, #MSG_EVENT             ; Status = message received

               PUSH  ?RTX_TMP2                  ; Restore return address
               PUSH  ?RTX_TMP1                  ; to stack

	       ; We must check for possible preemption
               CLR   ?RTX_FULL_CONTEXT
               JMP   ?RTX_REDISPATCH

DUMMY_END:    ; Just to satisfy the LIB-51
;
;   END OF MODULE
;
	END
